﻿//-----------------------------------------------------------------------
// <copyright file="WorkItemProperties.cs" company="Microsoft Corporation">
//     Copyright Microsoft Corporation. All Rights Reserved. This code released under the terms of the Microsoft Public License (MS-PL, http://opensource.org/licenses/ms-pl.html.) This is sample code only, do not use in production environments.
// </copyright>
//-----------------------------------------------------------------------
namespace VisualStudioOnlineOData.Models
{
    using System;
    using System.Globalization;
    using System.Linq;
    using System.Text.RegularExpressions;
    using VisualStudioOnlineOData.Utilities;

    /// <summary>
    ///
    /// </summary>
    public class WorkItemProperties
    {
        private const string key = "-FLIGHT-PLAN-META-DATA-";
        private static readonly string[] productOwnerToken = new[] { "PO", "PRODUCTOWNER" };
        private static readonly string[] projectLeadToken = new[] { "PL", "PROJECTLEAD" };
        private static readonly string[] ruckMasterToken = new[] { "RM", "RUCKMASTER" };
        private static readonly string[] scheduledStatusToken = new[] { "SS", "SCHEDULEDSTATUS" };
        private static readonly string[] scheduledToken = new[] { "SC", "SCHEDULED" };
        private static readonly string[] dateLandedToken = new[] { "DL", "DATELANDED" };
        private static readonly string[] statusToken = new[] { "S", "STATE" };
        private static readonly string[] versionToken = new[] { "V", "VERSION" };
        private static Regex KeyValueFinder = new Regex(@"(?<Name>[\w ]+)\s*\:(?<Value>[^\r\n]+)");
        private string description;

        /// <summary>
        /// Initializes a new instance of the <see cref="WorkItemProperties"/> class.
        /// </summary>
        public WorkItemProperties()
        {
            Scheduled = DateTimeOffsetTools.Base();
            DateLanded = null;
        }

        /// <summary>
        /// Gets or sets the assigned to.
        /// </summary>
        /// <value>
        /// The assigned to.
        /// </value>
        public string AssignedTo { get; set; }

        /// <summary>
        /// Gets or sets the description.
        /// </summary>
        /// <value>
        /// The description.
        /// </value>
        public string Description
        {
            get
            {
                return description;
            }
            set
            {
                if (value != description)
                {
                    description = value;
                }
            }
        }

        /// <summary>
        /// Gets or sets the identifier.
        /// </summary>
        /// <value>
        /// The identifier.
        /// </value>
        public int Id { get; set; }

        /// <summary>
        /// Gets the product owner.
        /// </summary>
        /// <value>
        /// The product owner.
        /// </value>
        public string ProductOwner { get; private set; }

        /// <summary>
        /// Gets or sets the project.
        /// </summary>
        /// <value>
        /// The project.
        /// </value>
        public string Project { get; set; }

        /// <summary>
        /// Gets the project lead.
        /// </summary>
        /// <value>
        /// The project lead.
        /// </value>
        public string ProjectLead { get; private set; }

        /// <summary>
        /// Gets the ruck master.
        /// </summary>
        /// <value>
        /// The ruck master.
        /// </value>
        public string RuckMaster { get; private set; }

        /// <summary>
        /// Gets or sets the scheduled.
        /// </summary>
        /// <value>
        /// The scheduled.
        /// </value>
        public DateTimeOffset Scheduled { get; set; }

        public DateTime? DateLanded { get; set; }

        /// <summary>
        /// Gets or sets the scheduled status.
        /// </summary>
        /// <value>
        /// The scheduled status.
        /// </value>
        public string ScheduledStatus { get; set; }

        /// <summary>
        /// Gets or sets the state.
        /// </summary>
        /// <value>
        /// The state.
        /// </value>
        public string State { get; set; }

        /// <summary>
        /// Gets the status.
        /// </summary>
        /// <value>
        /// The status.
        /// </value>
        public string Status { get; private set; }

        /// <summary>
        /// Gets or sets the type.
        /// </summary>
        /// <value>
        /// The type.
        /// </value>
        public string Type { get; set; }

        /// <summary>
        /// Gets or sets the version.
        /// </summary>
        /// <value>
        /// The version.
        /// </value>
        public string Version { get; set; }

        /// <summary>
        /// Gets or sets the web editor URL.
        /// </summary>
        /// <value>
        /// The web editor URL.
        /// </value>
        public string WebEditorUrl { get; set; }

        /// <summary>
        /// Updateds the description.
        /// </summary>
        /// <param name="input">The input.</param>
        /// <param name="newValues">The new values.</param>
        /// <returns></returns>
        public static string UpdatedDescription(string input, WorkItem newValues)
        {
            var description = "";

            if (input.IndexOf(key) > 0)
            {
                var temp = input;
                description = temp.Substring(0, temp.IndexOf(key));
                temp = temp.Remove(0, temp.IndexOf(key) + 23); // first
                temp = temp.Remove(0, temp.IndexOf(key) + 23); // second
                description += temp;
            }
            else
            {
                description = input;
            }

            description += key + "<br/>" + Environment.NewLine;
            description += versionToken.Last() + " : " + newValues.Version + "<br/>" + Environment.NewLine;
            description += statusToken.Last() + " : " + newValues.Status + "<br/>" + Environment.NewLine;
            description += productOwnerToken.Last() + " : " + newValues.ProductOwner + "<br/>" + Environment.NewLine;
            description += scheduledToken.Last() + " : " + newValues.Scheduled.ToString("yyyy-MM-dd", CultureInfo.CurrentCulture) + "<br/>" + Environment.NewLine;
            description += dateLandedToken.Last() + " : " + (newValues.DateLanded.HasValue ? newValues.DateLanded.Value.ToString("yyyy-MM-dd", CultureInfo.InvariantCulture) : "N/A") + "<br/>" + Environment.NewLine;
            description += scheduledStatusToken.Last() + " : " + newValues.ScheduledStatus + "<br/>" + Environment.NewLine;
            description += projectLeadToken.Last() + " : " + newValues.ProjectLead + "<br/>" + Environment.NewLine;
            description += ruckMasterToken.Last() + " : " + newValues.RuckMaster + "<br/>" + Environment.NewLine;
            description += key + "<br/>" + Environment.NewLine;

            return description;
        }

        private static string FixTokensWithNoValues(string descriptionSub)
        {
            string result = string.Empty;
            string temp = string.Empty;
            foreach (string line in descriptionSub.Split('\n'))
            {
                if (!string.IsNullOrEmpty(line))
                {
                    if (!string.IsNullOrEmpty(temp) && line.Contains(":"))
                    {
                        result += temp + "\n";
                        temp = string.Empty;
                    }
                    if (line.Trim().EndsWith(":"))
                    {
                        temp = line.Trim() + " ";
                    }
                    else
                    {
                        result += temp + line + "\n";
                        temp = string.Empty;
                    }
                }
            }
            if (!string.IsNullOrEmpty(temp))
            {
                result += temp + "\n";
                temp = string.Empty;
            }
            return result.Trim();
        }

        private void DeriveMetaInfo()
        {
            if (!string.IsNullOrWhiteSpace(this.State))
            {
                switch (this.State.ToUpperInvariant())
                {
                    case "IN PROGRESS":
                        {
                            this.Status = "InFlight";
                            break;
                        }
                    case "DONE":
                        {
                            this.Status = "Landed";
                            break;
                        }
                    case "NEW":
                        {
                            this.Status = "Idea";
                            break;
                        }
                    case "REMOVED":
                        {
                            this.Status = "Cancelled";
                            break;
                        }
                }
            }

            if (!string.IsNullOrWhiteSpace(this.AssignedTo))
            {
                this.ProductOwner = this.AssignedTo;
            }
        }

        /// <summary>
        /// Loads the meta information.
        /// </summary>
        public void LoadMetaInfo()
        {
            DeriveMetaInfo();
            ParseMetaInfo();
            PostUpdateMetaInfo();
        }

        private string MetaCleaner(string input)
        {
            var result = input.Replace("&nbsp;", " ");
            result = result.Trim();
            return result;
        }

        private void ParseMetaInfo()
        {
            var descriptionSub = Utilities.HtmlRemoval.StripTagsCharArray(this.description);

            if (string.IsNullOrEmpty(descriptionSub))
            {
                Status = null;
                ProductOwner = null;
                ProjectLead = null;
                RuckMaster = null;
                return;
            }

            var firstPosition = descriptionSub.IndexOf(key, 0);
            if (firstPosition < 0)
            {
                return;
            }

            descriptionSub = descriptionSub.Substring(firstPosition + 23);
            var secondPosition = descriptionSub.IndexOf(key, 0);
            if (secondPosition < 0)
            {
                return;
            }

            descriptionSub = descriptionSub.Substring(0, secondPosition);
            descriptionSub = FixTokensWithNoValues(descriptionSub);
            foreach (string line in descriptionSub.Split('\n'))
            {
                if (line.Trim().Length > 0)
                {
                    var match = KeyValueFinder.Match(line.Trim());
                    if (!match.Success)
                    {
                        continue;
                    }

                    string name = match.Groups["Name"].Value.Trim().ToUpperInvariant();
                    string value = match.Groups["Value"].Value.Trim();

                    if (string.IsNullOrWhiteSpace(this.ScheduledStatus) && scheduledStatusToken.Contains(name))
                    {
                        this.ScheduledStatus = MetaCleaner(value);
                        continue;
                    }

                    if (this.Scheduled == DateTimeOffsetTools.Base() && scheduledToken.Contains(name))
                    {
                        try
                        {
                            this.Scheduled = DateTimeOffset.ParseExact(value, "yyyy-MM-dd", CultureInfo.InvariantCulture);
                        }
                        catch (FormatException)
                        {
                            this.Scheduled = DateTimeOffsetTools.Base();
                        }
                        continue;
                    }

                    if (string.IsNullOrWhiteSpace(this.ProductOwner) && productOwnerToken.Contains(name))
                    {
                        this.ProductOwner = MetaCleaner(value);
                        continue;
                    }

                    if (string.IsNullOrWhiteSpace(this.ProjectLead) && projectLeadToken.Contains(name))
                    {
                        this.ProjectLead = MetaCleaner(value);
                        continue;
                    }

                    if (string.IsNullOrWhiteSpace(this.RuckMaster) && ruckMasterToken.Contains(name))
                    {
                        this.RuckMaster = MetaCleaner(value);
                        continue;
                    }

                    if (string.IsNullOrWhiteSpace(this.Status) && statusToken.Contains(name))
                    {
                        this.Status = MetaCleaner(value);
                        continue;
                    }

                    if (string.IsNullOrWhiteSpace(this.Version) && versionToken.Contains(name))
                    {
                        this.Version = MetaCleaner(value);
                        continue;
                    }

                    if (!this.DateLanded.HasValue && dateLandedToken.Contains(name) && value != "N/A")
                    {
                        try
                        {
                            this.DateLanded = DateTime.ParseExact(value, "yyyy-MM-dd", CultureInfo.InvariantCulture);
                        }
                        catch (FormatException)
                        {
                            this.DateLanded = null;
                        }
                        continue;
                    }
                }
            }
        }

        private void PostUpdateMetaInfo()
        {
            if (this.Scheduled != DateTimeOffsetTools.Base() && this.Scheduled < DateTimeOffset.UtcNow)
            {
                if (!string.IsNullOrWhiteSpace(this.Status) && this.Status.Equals("Landed", StringComparison.CurrentCultureIgnoreCase))
                {
                    return;
                }

                this.Status = "Delayed";
            }
        }
    }
}